﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StableDiffusionTools.RTF
{
    //https://msdn.microsoft.com/en-us/library/aa140301.aspx#rtfspec_8
    //syncRTBCurrent.Rtf = @"{\rtf1\pc \b 02/11/2010 - 05:15 PM - Adam:\b0 Another test notes added on 2nd November\par \b 02/11/2010 - 05:14 PM - Z_kas:\b0 Test Notes. STAGE CHANGED TO: N Enq - Send Quote\par \b 02/11/2010 - 05:12 PM - user32:\b0 Another test notes added on 2nd November";
    //{\f1\cb1\cf2 This is colored text. The background is color 1 and the foreground is color 2.}
    public class RTFormatter
    {
        private StringBuilder body;
        private List<Color> colors;
        private int textColorIndex = -1;
        private int backColorIndex = -1;
        bool justInsertedColorCode = false;

        public int DefaultFontSize { get; set; } = 17;
        public string DefaultFontName { get; set; } = "Microsoft Sans Serif";

        private TextFormatting formatting = TextFormatting.Normal;
        public TextFormatting Formatting
        {
            get
            {
                return formatting;
            }
            set
            {
                if (formatting != value)
                {
                    SetFormatting(value);
                }
            }
        }

        //bool justInsertedText = false;
        public Color TextColor
        {
            get
            {
                if (textColorIndex == -1)//|| colors.Count == 0)
                    return Color.Black;

                return colors[textColorIndex];
            }
            set
            {
                SetTextColor(value);
            }
        }
        public Color BackColor
        {
            get
            {
                if (backColorIndex == -1)//|| colors.Count == 0)
                    return Color.White;

                return colors[backColorIndex];
            }
            set
            {
                SetBackColor(value);
            }
        }


        public RTFormatter()
        {
            body = new StringBuilder();
            colors = new List<Color>();
        }

        //Public Methods
        public void AppendText(string text)
        {
            if (string.IsNullOrEmpty(text)) throw new NullReferenceException("text is null or empty");
            text = text.Replace(@"\", @"\\"); //escape slash
            text = text.Replace(Environment.NewLine, @"\par ");
            text = text.Replace("\n", @"\par ");
            text = text.Replace("\r", @"\par ");

            if (justInsertedColorCode)
                body.Append(string.Format(" {0}", text));
            else
                body.Append(text);

            justInsertedColorCode = false;
        }

        public void AppendLine(string text = "")
        {
            AppendText("\n" + text);
            //body.Append(string.Format("{0}\\par ", append));
        }

        public void AppendLink(string text, string url)
        {
            if (string.IsNullOrEmpty(text)) throw new NullReferenceException("text is null or empty");
            text = text.Replace(@"\", @"\\"); //escape slash
            text = text.Replace(Environment.NewLine, @"\par ");
            text = text.Replace("\n", @"\par ");
            text = text.Replace("\r", @"\par ");
            //{\colortbl ;\red0\green0\blue238;}
            //{\field{\*\fldinst HYPERLINK "URL"} {\fldrslt{ \ul\cf1Text to display} } }
            int hyperColorIndex = GetorCreateColorIndex(Color.LightCyan);
            //"{{\\field{{\\*\\fldinst HYPERLINK \"{0}\"}} {{\\fldrslt{{ \\ul\\cf{1}{2}}} }} }}"
            string rtfText = string.Format("{{\\field{{\\*\\fldinst{{HYPERLINK \"{0}\"}}}}{{\\fldrslt{{\\ul\\cf{1} {2}}}}}}}", url, hyperColorIndex, text);
            //
            if (justInsertedColorCode)
                body.Append(string.Format(" {0}", rtfText));
            else
                body.Append(rtfText);

            justInsertedColorCode = false;
        }

        //Private Methods
        private void SetTextColor(Color newColor)
        {//TODO: its possible to add/set color without any text using it later, buffer the color for use when appending text?
            int index = GetColorIndex(newColor);

            if (index == -1)//no color found, add color, set index
            {
                //if (textColorIndex != -1) {/*body.Append("}");*/}//old color in effect, close it
                textColorIndex = colors.Count;
                colors.Add(newColor);
                body.Append(string.Format("\\cf{0}", textColorIndex + 1));
                justInsertedColorCode = true;
            }
            else if (index != textColorIndex && TextColor != newColor)//new color
            {
                //if (textColorIndex != -1) {/*body.Append("\\cf0");*/}//old color in effect, close it?
                textColorIndex = index;
                body.Append(string.Format("\\cf{0}", textColorIndex + 1));
                justInsertedColorCode = true;
            }
        }

        private int GetorCreateColorIndex(Color color)
        {
            int index = GetColorIndex(color);
            if (index == -1)//no color found, add color, set index
            {
                index = colors.Count;
                colors.Add(color);
            }
            return index;
        }

        private void SetBackColor(Color newColor)
        {
            int index = GetColorIndex(newColor);

            if (index == -1)//no color found, add color, set index
            {
                //if (backColorIndex != -1) {/*body.Append("}");*/}//old color in effect, close it
                backColorIndex = colors.Count;
                colors.Add(newColor);
                body.Append(string.Format("\\highlight{0}", backColorIndex + 1));
                justInsertedColorCode = true;
            }
            else if (index != backColorIndex && BackColor != newColor)//found color!
            {
                //if (backColorIndex != -1) {/*body.Append("\\cf0");*/}//old color in effect, close it?
                backColorIndex = index;
                body.Append(string.Format("\\highlight{0}", backColorIndex + 1));
                justInsertedColorCode = true;
            }
        }

        private void SetFormatting(TextFormatting fmt)
        {
            if (formatting != TextFormatting.Normal)
                body.Append(GetFormattingString(formatting, false));
            if (fmt != TextFormatting.Normal)
                body.Append(string.Format("{0}", GetFormattingString(fmt)));

            formatting = fmt;
        }

        private string GetFormattingString(TextFormatting fmt, bool start = true)
        {
            string result = "";
            switch (fmt)
            {
                case TextFormatting.Italics:
                    result = "\\i";
                    break;
                case TextFormatting.Bold:
                    result = "\\b";
                    break;
                case TextFormatting.StrikeThrough:
                    result = "\\strike";
                    break;
                case TextFormatting.Outline:
                    result = "\\outl";
                    break;
                case TextFormatting.Underline:
                    result = "\\ul";
                    break;
                case TextFormatting.Normal:
                default:
                    break;
            }
            if (!start && result != "")
                result += "0";
            return result;
        }

        //Public Overrides
        public string ToString(string fontName)
        {
            //TODO: take more control on viewkind, fontsize etc
            Formatting = TextFormatting.Normal;
            DefaultFontName = fontName;
            return string.Format("{0}{1}\\viewkind4\\uc1 \\pard\\f0\\fs{2} {3}", CreateFormat(), CreateColorTable(), DefaultFontSize, body.ToString());
        }
        public override string ToString()
        {
            //TODO: take more control on viewkind, fontsize etc
            Formatting = TextFormatting.Normal;
            return string.Format("{0}{1}\\viewkind4\\uc1 \\pard\\f0\\fs{2} {3}", CreateFormat(), CreateColorTable(), DefaultFontSize, body.ToString());
        }

        //Private Methods
        private int GetColorIndex(Color newColor)
        {
            int index = -1;
            for (int i = 0; i < colors.Count; i++)
            {
                if (colors[i] == newColor)
                {
                    index = i;
                    break;
                }
            }
            return index;
        }

        private string CreateColorTable()
        {
            StringBuilder ct = new StringBuilder();

            if (colors.Count > 0)
            {

                ct.Append("{{\\colortbl ;");
                for (int i = 0; i < colors.Count; i++)
                {
                    Color c = colors[i];
                    ct.Append(string.Format("\\red{0}\\green{1}\\blue{2};", c.R, c.G, c.B));
                }
                ct.Append("}}");
            }

            return ct.ToString();
        }

        private string CreateFormat()
        {
            return string.Format("{{\\rtf1\\ansi\\ansicpg1252\\deff0\\nouicompat\\deflang1033{0}", CreateFontTable());
        }

        private string CreateFontTable()
        {
            return string.Format("{{\\fonttbl{{\\f0\\fnil\\fcharset0 {0};}}}}", DefaultFontName);
        }
    }
}
